package model.arbolBSharp;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import model.extras.FrontCoding;
import model.registro.RegistroHoja;

public class Hoja {

	private List<RegistroHoja> listaRegistroHoja;

	private Integer posicionEnArchivo;

	private Integer punteroASiguiente;

	public void setPunteroASiguiente(Integer punteroASiguiente) {
		this.punteroASiguiente = punteroASiguiente;
	}

	public void setPosicionEnArchivo(Integer posicionEnArchivo) {
		this.posicionEnArchivo = posicionEnArchivo;
	}

	public Hoja() {
		listaRegistroHoja = new ArrayList<RegistroHoja>();
	}

	public Hoja(List<RegistroHoja> listaRegistroHojaaux,
			Integer posicionEnArchivo, Integer punteroASiguiente) {
		this.listaRegistroHoja = listaRegistroHojaaux;
		this.posicionEnArchivo = posicionEnArchivo;
		this.punteroASiguiente = punteroASiguiente;
	}

	public List<RegistroHoja> getListaRegistroHoja() {
		return this.listaRegistroHoja;
	}

	public Integer getPosicionEnArchivo() {
		return this.posicionEnArchivo;
	}

	public Integer getPunteroASiguiente() {
		return this.punteroASiguiente;
	}
	/**@return Metodo void, inserta el registro si no existe o actualiza el offset y el peso global
	 * @param String: palabra a insertar; 
	 *        int: offset a la lista de documentos; 
	 *        float: peso global de la palabra; 
	 */
	
	public void insertar(String palabra, int offset, float pesoGlobal) {
		if (this.buscar(palabra)==false){
			RegistroHoja reg = new RegistroHoja();
			RegistroHoja reg2 = new RegistroHoja();
			String str;
			RegistroHoja[] listaAux = new RegistroHoja[listaRegistroHoja.size()+1];
			reg.setCantCaractNuevos(palabra.length());
			reg.setCantCaractRepetidos(0);
			reg.setOffset(offset);
			reg.setPesoGlobal(pesoGlobal);
			reg.setTerm(palabra);
			listaAux[listaRegistroHoja.size()] = reg;
			
			//cargo los registros de a uno en la lista auxilar, con los terminos expandidos
			if (listaRegistroHoja.isEmpty()==false){
				for (int i = 0; i < listaRegistroHoja.size(); i++) {
					reg=listaRegistroHoja.get(i);
					reg2= (RegistroHoja)reg.clone();
					str = this.getPalabra(reg2);
					reg2.setTerm(str);
					listaAux[i] = reg2;
				}
			}
		    // ordeno los registros con las palabras expandidas y vacio la lista original
			Arrays.sort(listaAux);
		    listaRegistroHoja.clear();
			reg = listaAux[0];
			reg.setCantCaractNuevos(reg.getTermino().length());
			reg.setCantCaractRepetidos(0);
			str=reg.getTermino();
			listaRegistroHoja.add(0, reg);		
			if (listaAux.length > 1) {
				FrontCoding fg = new FrontCoding();
				int i = 1;
				do {
					reg = listaAux[i];								                
					fg.compacto(str, reg.getTermino());				
                    // guardo el termino expandido en la variable str antes de perderlo
					str = reg.getTermino();
					reg.setCantCaractNuevos(fg.getNuevos());
					reg.setCantCaractRepetidos(fg.getAnteriores());
					reg.setTerm(fg.getTermino());
					this.listaRegistroHoja.add(i,reg);						
					i++;
				} while (i < listaAux.length);
			}
				
		}
		else{
			RegistroHoja reg = new RegistroHoja();
			reg = this.buscarRegistro(palabra);
			reg.setOffset(offset);
			reg.setPesoGlobal(pesoGlobal);
		}
	}


	/**
	 * @param RegistroHoja reg
	 * @return devuelve la palabra entera del registro, si no la encuentra devuelve nulo  
	 */
	public String getPalabra(RegistroHoja reg) {
		if (listaRegistroHoja.isEmpty())
			return "";
			//return reg.getTermino();
		else {
			RegistroHoja reg1 = new RegistroHoja();
			FrontCoding fg;
			String termino1 = null;
			String aux = null;
			int i = 0;
			reg1 = listaRegistroHoja.get(0);
			termino1 = reg1.getTermino();
			if (reg1.equals(reg))
				return termino1;
			else {
				for (i = 0; i < listaRegistroHoja.size() - 1; i++) {
					reg1 = listaRegistroHoja.get(i + 1);
					fg = new FrontCoding();
				 	aux = fg.descompacto(reg1.getCantCaractRepetidos(), reg1.getCantCaractNuevos(), termino1, reg1.getTermino());
				   	if (reg1.equals(reg))
						return aux;
					else 				
						termino1 = aux;
				}
			}
			return "";
			//return reg.getTermino();
		}
	}


	public int cantidadRegistros() {
		return listaRegistroHoja.size();
	}

	/**
	 * @param String terminoClave
	 * @return true si existe el termino comleto (apicando Front Coding) en la hoja
	 */
	public boolean buscar(String terminoClave) {
		if (listaRegistroHoja.isEmpty())
			return false;
		else {
			String termino1;
			for (int i = 0; i <this.listaRegistroHoja.size(); i++){
				termino1 = this.getPalabra(this.listaRegistroHoja.get(i));
				if (termino1.equals(terminoClave))
							return true;
				}
			return false;
		}
	}
	
	
	/**
	 * @param String terminoClave
	 * @return Dado una palabra, devuelve el registro que contiene esa
	 *         palabra aplicando Front Coding; sino devuelve null
	 */
	public RegistroHoja buscarRegistro(String terminoClave) {
		if (listaRegistroHoja.isEmpty())
			return null;
		else {
			String termino1;
			if (!this.getListaRegistroHoja().isEmpty())
				for (int i = 0; i <this.listaRegistroHoja.size(); i++){
					termino1 = this.getPalabra(this.listaRegistroHoja.get(i));
						if (termino1.equals(terminoClave))
							return this.listaRegistroHoja.get(i);
				}
		}				
		return null;
	}
	
	/*
	 *Divide la hoja en tres, nodoD (debe tener su posicion en archivo definida)
	 *es la hoja a la derecha de la hoja a dividir, 
	 *nodoI es la futura hoja izquierda y nodoDNuevo es la futura hoja a la derecha 
	 */
	public void dividirHoja(Hoja nodoI, Hoja nodoD, Hoja nodoDNuevo) throws HojaException {
		
		if(nodoD.getPosicionEnArchivo()==null)
			throw new HojaException("El nodo de la derecha debe tener seteado su puntero en archivo");
		
		RegistroHoja regAux = null;
		String palabra = null;
		for(int i=0; i<nodoD.getListaRegistroHoja().size(); i++){
			regAux= (RegistroHoja)nodoD.getListaRegistroHoja().get(i).clone();
			palabra = nodoD.getPalabra(regAux);
			this.insertar(palabra, regAux.getOffset(), regAux.getPesoGlobal());
		}
		nodoD.getListaRegistroHoja().clear();
		int cantClaves = this.listaRegistroHoja.size();
		int i = 0;		
		while(i<cantClaves*1/3){
			regAux = (RegistroHoja) this.getListaRegistroHoja().get(i).clone();
			palabra =this.getPalabra(regAux);
			nodoI.insertar(palabra, regAux.getOffset(), regAux.getPesoGlobal());
			i++;
		}
		while((i>=cantClaves*1/3)&&(i<cantClaves*2/3)){
			regAux = (RegistroHoja) this.getListaRegistroHoja().get(i).clone();
			palabra =this.getPalabra(regAux);
			nodoD.insertar(palabra, regAux.getOffset(), regAux.getPesoGlobal());
			i++;
		}
		while((i>=cantClaves*2/3)&&(i<cantClaves)){
			regAux = (RegistroHoja) this.getListaRegistroHoja().get(i).clone();
			palabra =this.getPalabra(regAux);
			nodoDNuevo.insertar(palabra, regAux.getOffset(), regAux.getPesoGlobal());
			i++;
		}
		nodoI.setPosicionEnArchivo(this.getPosicionEnArchivo());
		nodoI.setPunteroASiguiente(nodoD.getPosicionEnArchivo());
		nodoDNuevo.setPunteroASiguiente(nodoD.getPunteroASiguiente());
		nodoD.setPunteroASiguiente(nodoDNuevo.getPosicionEnArchivo());
	}
			
	/**
	 * @param RegistroHoja regABorrar
	 * @return true si puede borrar el registro en la hoja; sino devuelve false
	 */
	public boolean removerRegistro(RegistroHoja regABorrar) {
			
		//con "getPalabra" busco la palabra expandida del registro en la hoja
		// con "buscar" me fijo que esa palabra realmente exista en la hoja
		if (this.buscar(this.getPalabra(regABorrar))){
			RegistroHoja reg = new RegistroHoja();
			RegistroHoja reg2 = new RegistroHoja();
			int j = 0;
			String str;
			// creo una lista de tamanio menor xq se que existe y voy a eliminar un registro
			RegistroHoja[] listaAux = new RegistroHoja[this.listaRegistroHoja.size() - 1];
			
			for (int i = 0; i < this.listaRegistroHoja.size(); i++) {
				reg = (RegistroHoja) this.listaRegistroHoja.get(i);
				reg2 = (RegistroHoja) reg.clone();
				//en "str" expando la palabra del registro y la guardo expandida en un registro aux;
				//inserto el registro con la palabra expandida en la lista aux 
				str = this.getPalabra(reg2);
				
				if (reg.equals(regABorrar) == false) {
			    	reg2.setTerm(str);
					listaAux[j]=reg2;
					j++;					
					}
			}
			
			listaRegistroHoja.clear();
			for (int i =0; i<listaAux.length;i++){
				reg2=listaAux[i];
				this.insertar(reg2.getTermino(), reg2.getOffset(), reg2.getPesoGlobal());
			}
			return true;
		}
		else 
			return false;
	}

	/*
	 * Este metodo devuelve el primer registro de la lista,
	 * o nulo si esta vacia
	 */
	public RegistroHoja getMenorRegistro() {
		if (listaRegistroHoja.isEmpty())
			return null;
		else
			return listaRegistroHoja.get(0);
	}
	/*
	 * Este metodo devuelve el primer registro de la lista,
	 * o nulo si esta vacia
	 */
	public RegistroHoja getMayorRegistro() {
		if (listaRegistroHoja.isEmpty())
			return null;
		else
			return listaRegistroHoja.get(listaRegistroHoja.size() - 1);
	}
}